#ifndef _ODN_VECTOR3D_H
#define _ODN_VECTOR3D_H

//Standard library headers
#include <math.h>
#include <iostream>

#define PI 3.14159265358979323846;

namespace ouden
{
	namespace utility
	{		
		
		//Operator definitions
		//  Cross pruduct = 	^
		//  Dot product =   	%
		//  2D cross product = 	*
		
		class Vector3D
		{
		
		public:
		
			//Scaler type definition
			typedef float scalar;
		
		private:
		    
		    //Array to hold vector data
			scalar V[3];
			
		public:
		
			//Initalisation constructor
			Vector3D(scalar X = 0, scalar Y = 0, scalar Z = 0) { V[0]=X; V[1]=Y; V[2]=Z; }
			
			
			//Array access operator
			scalar operator [] (const unsigned i) const	{ return V[i]; }
			scalar &operator [] (const unsigned i) { return V[i]; }
			
			//Copy constructor
			Vector3D(const Vector3D &vec) { V[0]=vec[0]; V[1]=vec[1]; V[2]=vec[2]; }
			
			//Function to return vector array
			scalar* Array()					{ return V; }
			
			//Element access functions
			scalar x()	const				{ return V[0]; }
			scalar y()	const				{ return V[1]; }
			scalar z()	const				{ return V[2]; }
			scalar &x()						{ return V[0]; }
			scalar &y()						{ return V[1]; }
			scalar &z()						{ return V[2]; }
			
			//Vector assignment
			const Vector3D &operator = (const Vector3D &vec)
			{
				V[0] = vec[0];
				V[1] = vec[1];
				V[2] = vec[2];
				
				return *this;
			}
			
			//Vector equality
			const bool operator == (const Vector3D &vec) const
			{
			    return ((V[0] == vec[0]) && (V[1] == vec[1]) && (V[2] == vec[2]));
			}
			
			//Zero test
			const bool isZero()		{ return ((V[0] == 0) && (V[1] == 0) && (V[2] == 0)); }
			
			//TODO: Think about using this 
			/*
			//Scalar equality
			const bool operator == (const scalar &s) const
			{
			    return ((V[0] == s) && (V[1] == s) && (V[2] == s));
			}
			*/
			//Vector inequality
			const bool operator != (const Vector3D &vec) const
			{
			    return !(*this == vec);
			}
			
			
			//Vector add
			const Vector3D operator + (const Vector3D &vec) const
			{
		 	    return Vector3D(V[0] + vec[0], V[1] + vec[1], V[2] + vec[2]);
			}
			
			//Vector add (opposite of negation)
			const Vector3D operator + () const
			{    
			    return Vector3D(*this);
			}
			
			//Vector increment
			const Vector3D& operator += (const Vector3D& vec)
			{    
		 	    V[0] += vec[0];
			  	V[1] += vec[1];
			  	V[2] += vec[2];
			  	
			  	return *this;
			}
			
			//Vector subtraction
			const Vector3D operator - (const Vector3D& vec) const
			{    
			    return Vector3D(V[0] - vec[0], V[1] - vec[1], V[2] - vec[2]);
			}
			
			//Vector negation
			const Vector3D operator - () const
			{    
			   return Vector3D(-V[0], -V[1], -V[2]);
			}
			
			//Vector decrement
			const Vector3D &operator -= (const Vector3D& vec)
			{
			    V[0] -= vec[0];
			    V[1] -= vec[1];
			    V[2] -= vec[2];
			
			    return *this;
			}
			
			//scalar self-multiply
			const Vector3D &operator *= (const scalar &s)
			{
			    V[0] *= s;
			    V[1] *= s;
			    V[2] *= s;
			  
			    return *this;
			}
			
			//Scalar self-divide
			const Vector3D &operator /= (const scalar &s)
			{
			    const float recip = 1/s; // for speed, one divecision
			
			    V[0] *= recip;
			    V[1] *= recip;
			    V[2] *= recip;
			
			    return *this;
			}
			
			//Post multiply by scalar
			const Vector3D operator * (const scalar &s) const
			{
			    return Vector3D(V[0]*s, V[1]*s, V[2]*s);
			}
			
			//Pre multiply by scalar
			friend inline const Vector3D operator*(const scalar &s, const Vector3D &vec)
			{
			    return vec*s;
			}
			
			//Divide by scalar
			const Vector3D operator / (scalar s) const
			{
			    s = 1/s;
			
			    return Vector3D(s*V[0], s*V[1], s*V[2]);
			}
			
			
			//Componentwise divide by vector
			const Vector3D operator / (const Vector3D &vec) const
			{
			    return Vector3D(V[0]/vec[0], V[1]/vec[1], V[2]/vec[2]);
			}
			
			
			//Cross product
			const Vector3D CrossProduct(const Vector3D &vec) const
			{
			    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
			}
			
			//Cross product
			const Vector3D operator ^ (const Vector3D &vec) const
			{
			    return Vector3D(V[1]*vec[2] - V[2]*vec[1], V[2]*vec[0] - V[0]*vec[2], V[0]*vec[1] - V[1]*vec[0]);
			}
			
			//Dot product
			const scalar DotProduct(const Vector3D &vec) const
			{
			    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
			}
			
			//Dot product
			const scalar operator % (const Vector3D &vec) const
			{
			    return V[0]*vec[0] + V[1]*vec[1] + V[2]*vec[2];
			}
			
			//2D cross product
			const scalar operator * (const Vector3D &vec) const
			{
			    return (V[0]*vec[1] - V[1]*vec[0]);
			}
			
			//Length of vector
			const scalar Length() const
			{
			  return (scalar)sqrt((double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]));
			}
			
			//Length squared of vector
			const scalar Length2() const
			{
			  return (scalar)(double)(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
			}
			
			//Return the unit vector
			const Vector3D UnitVector() const
			{
			  return (*this) / Length();
			}
			
			//Return the 2D normal vector
			const Vector3D Normal() const
			{
			  return Vector3D(V[1], -V[0], 0);
			}
			
			//Normalise this vector
			void Normalize()
			{
			  (*this) /= Length();
			}
			
			//Return angle of vector in degrees
			const scalar Angle() const
			{
				
				if (V[0] == 0){
					if (V[1] > 0)
						return -90;
					else
						return 90;
				}
				
				scalar a = (scalar)atan((double)(V[1] / V[0])) * 180 / PI;
				
			    if (V[0] > 0.0f)
					return (a + 180);
				else
					return a;
			}
			
			/*//Return vector with specified length
			const Vector3D operator | (const scalar length) const
			{
			  return *this * (length / !(*this));
			}*/
			
			/*// set length of Vector3D equal to length
			const Vector3D& operator |= (const float length)
			{
			  return *this = *this | length;
			}*/
			
			// return angle between two vectors
			/*const float inline Angle(const Vector3D& normal) const
			{
			     return acosf(*this % normal);
			}*/
			
			// reflect this vector off surface with normal vector
			/*const Vector3D inline Reflection(const Vector3D& normal) const
			{
			     const Vector3D vec(*this | 1);     // normaliV[2]e this Vector3D
			     return (vec - normal * 2.0 * (vec % normal)) * !*this;
			}*/
		
		};
		
	} //namespace utility
} //namespace ouden

#endif //_ODN_VECTOR3D_H
